'use strict';

const mosca = require('mosca');

const Utils = require('../utils');
const logger = Utils.logger(__filename);

module.exports = class Server {
  constructor(cmd, notify) {
    const port = ~~cmd.port;
    const serverId = this.serverId = cmd.name;
    this.port = port;
    this.clients = {};
    this.notify = (msg, err) => {
      if (err) {
        logger.warn(msg, err);
      } else {
        logger.trace(msg);
      }
      notify(msg);
    };

    let server;
    try {
      server = this.server = new mosca.Server({
        port
      });
    } catch (err) {
      this.notify(`${serverId} on port ${port} error`, err);
    }

    server.on('ready', () => {
      this.notify(`${serverId} on port ${port} is ready`);
    });

    server.on('error', (err) => {
      this.notify(`${serverId} on port ${port} error`, err);
    });

    // Client connected
    server.on('clientConnected', (client) => {
      this.notify(`Client connected: ${client.id}`);
      this.clients[client.id] = client;
    });

    // Client disconnected
    server.on('clientDisconnected', (client) => {
      this.notify(`Client disconnected: ${client.id}`);
      this.clients[client.id] = undefined;
    });

    // Client published
    server.on('published', (packet, client) => {
      packet.payload && (packet.payload = packet.payload.toString());
      const sender = client && client.id || (packet.topic.indexOf('$SYS') == 0 ? '$SYS' : serverId);
      this.notify(`${sender} published: ${JSON.stringify(packet)}`);
    });

    // Client subscribed
    server.on('subscribed', (topic, client) => {
      this.notify(`${client.id} subscribed: ${topic}`);
    });

    // Client unsubscribed
    server.on('unsubscribed', (topic, client) => {
      this.notify(`${client.id} unsubscribed: ${topic}`);
    });
  }

  publish(options) {
    this.notify(`${this.serverId} published topic ${options.topic} message: ${options.payload}`);
    this.server.publish(options);
  }

  close(callback) {
    this.server.close(() => {
      this.notify(`${this.serverId} on port ${this.port} closed`);
      callback && callback();
    });
  }
};
